package com.googlecode.placesapiclient.client.argument.validator;

import com.googlecode.placesapiclient.client.argument.annotation.ArgumentMapping;
import com.googlecode.placesapiclient.client.argument.annotation.ArgumentMappings;
import com.googlecode.placesapiclient.client.service.ServiceName;
import com.googlecode.placesapiclient.client.exception.ErrorCode;
import com.googlecode.placesapiclient.client.exception.ErrorCodeException;
import org.apache.log4j.Logger;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;


/**
 * Validator that checks ArgumentMap filling correctness according to ArgumentMapping.
 *
 * @author Łukasz Opaluch
 */
public class ArgumentMapValidator {

    private static final Logger logger = Logger.getLogger(ArgumentMapValidator.class.getName());

    public static void processAnnotations(Object obj, ServiceName serviceName) throws ErrorCodeException {

            Class cl = obj.getClass();

            // Checking all the fields for annotations
            for (Field f : cl.getDeclaredFields()) {

                try {
                    // Processing all the annotations on a single field
                    for (Annotation annotation : f.getAnnotations()) {

                        if (annotation.annotationType() == ArgumentMappings.class) {
                            ArgumentMappings argumentMappings = (ArgumentMappings) annotation;
                            // Setting the field to be accessible from our class is it is a private member of the class under processing
                            // The setAccessible method will not work if you have Java SecurityManager configured and active.
                            f.setAccessible(true);

                            for (ArgumentMapping argumentMapping : argumentMappings.mappings()) {
                                if (argumentMapping.serviceName().equals(serviceName)) {
                                    checkArgumentMappingScope(obj, serviceName, f, argumentMapping);
                                }
                            }

                        }
                    }
                } catch (IllegalAccessException e) {
                    logger.error("IllegalAccessException during validating argument mapping", e);
                    throw new ErrorCodeException(ErrorCode.UNKNOWN_EXCEPTION, e.getLocalizedMessage());
                }
            }

    }

    private static void checkArgumentMappingScope(Object obj, ServiceName serviceName, Field f, ArgumentMapping argumentMapping) throws IllegalAccessException, ErrorCodeException {
        switch (argumentMapping.scope()) {
            case REQUIRED:
                if (f.get(obj) == null) {
                    throw new ErrorCodeException(ErrorCode.INVALID_PARAMETER, "The value of the field " + f.toString() + " can't be NULL.");
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Required parameter: " + argumentMapping.mapping() + ", value: " + String.valueOf(f.get(obj)));
                    }
                }
                break;
            case OPTIONAL:
                if (f.get(obj) != null) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Optional parameter: " + argumentMapping.mapping() + ", value: " + String.valueOf(f.get(obj)));
                    }
                }
                break;
            case NOT_SUPPORTED:
                if (f.get(obj) != null) {
                    logger.warn("Not supported parameter: " + argumentMapping.mapping() + ", value: " + String.valueOf(f.get(obj)) + " for service: " + serviceName.name());
                }
                break;
        }
    }
}